﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BuildOptimizer
{
    class BoDecisionAnalyzer
    {
        //GUI functionality
        private GUI _GUI = null;
        public void registerGUI(GUI GUI)
        {
            _GUI = GUI;
        }
        public void cancelAllWork()
        {
            _decisionTree.cancelAllWork();
        }

        private BoGoal _goal = null;
        private BoDecisionTree _decisionTree = null;

        private int _nbGamesPlayed = 0;
        private int _nbGamesPlayedPublished = 0;
        private BoBuildOrder _bestBuildOrder = null;

        public BoDecisionAnalyzer(BoGoal goal)
        {
            _goal = goal;
        }
        public void start(int nbThreadsDesired)
        {
            _decisionTree = new BoDecisionTree(this);
            _decisionTree.registerGUI(_GUI);
            _decisionTree.buildAndTraverse(nbThreadsDesired);
            printBestBuildOrder();
        }

        public List<BoCommand> findPossibleCommands(BoDecisionNode node)
        {
            List<BoCommand> possibleCommands = new List<BoCommand>();

            //units
            for (int i = 0; i < node.gameState.nbHatcheries; i++)
            {
                if (node.isCommandRequired[(int)commandID.buildCorruptor] && node.isCommandUnlocked[(int)commandID.buildCorruptor] && BoCommandBuildCorruptor.isAffordable(node.gameState) && BoCommandBuildCorruptor.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildCorruptor(i)); }
                if (node.isCommandRequired[(int)commandID.buildHydralisk] && node.isCommandUnlocked[(int)commandID.buildHydralisk] && BoCommandBuildHydralisk.isAffordable(node.gameState) && BoCommandBuildHydralisk.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildHydralisk(i)); }
                if (node.isCommandRequired[(int)commandID.buildInfestor] && node.isCommandUnlocked[(int)commandID.buildInfestor] && BoCommandBuildInfestor.isAffordable(node.gameState) && BoCommandBuildInfestor.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildInfestor(i)); }
                if (node.isCommandRequired[(int)commandID.buildMutalisk] && node.isCommandUnlocked[(int)commandID.buildMutalisk] && BoCommandBuildMutalisk.isAffordable(node.gameState) && BoCommandBuildMutalisk.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildMutalisk(i)); }
                if (node.isCommandRequired[(int)commandID.buildOverlord] && node.isCommandUnlocked[(int)commandID.buildOverlord] && BoCommandBuildOverlord.isAffordable(node.gameState) && BoCommandBuildOverlord.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildOverlord(i)); }
                if (node.isCommandRequired[(int)commandID.buildQueen] && node.isCommandUnlocked[(int)commandID.buildQueen] && BoCommandBuildQueen.isAffordable(node.gameState) && BoCommandBuildQueen.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildQueen(i)); }
                if (node.isCommandRequired[(int)commandID.buildRoach] && node.isCommandUnlocked[(int)commandID.buildRoach] && BoCommandBuildRoach.isAffordable(node.gameState) && BoCommandBuildRoach.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildRoach(i)); }
                if (node.isCommandRequired[(int)commandID.buildUltralisk] && node.isCommandUnlocked[(int)commandID.buildUltralisk] && BoCommandBuildUltralisk.isAffordable(node.gameState) && BoCommandBuildUltralisk.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildUltralisk(i)); }
                if (node.isCommandRequired[(int)commandID.buildZergling] && node.isCommandUnlocked[(int)commandID.buildZergling] && BoCommandBuildZergling.isAffordable(node.gameState) && BoCommandBuildZergling.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildZergling(i)); }
            }
            for (int i = 0; i < node.gameState.nbHatcheries; i++)
            {
                for (int j = 0; j < node.gameState.nbHatcheriesAtResources; j++)
                {
                    if (node.isCommandRequired[(int)commandID.buildDrone] && node.isCommandUnlocked[(int)commandID.buildDrone] && BoCommandBuildDrone.isAffordable(node.gameState) && BoCommandBuildDrone.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildDrone(i, j)); }
                }
            }

            if (node.isCommandRequired[(int)commandID.buildBaneling] && node.isCommandUnlocked[(int)commandID.buildBaneling] && BoCommandBuildBaneling.isAffordable(node.gameState) && BoCommandBuildBaneling.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandBuildBaneling()); }
            if (node.isCommandRequired[(int)commandID.buildBroodlord] && node.isCommandUnlocked[(int)commandID.buildBroodlord] && BoCommandBuildBroodlord.isAffordable(node.gameState) && BoCommandBuildBroodlord.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandBuildBroodlord()); }
            if (node.isCommandRequired[(int)commandID.buildOverseer] && node.isCommandUnlocked[(int)commandID.buildOverseer] && BoCommandBuildOverseer.isAffordable(node.gameState) && BoCommandBuildOverseer.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandBuildOverseer()); }

            //queens
            if (node.isCommandRequired[(int)commandID.queenCreepTumor] && node.isCommandUnlocked[(int)commandID.queenCreepTumor] && BoCommandQueenCreepTumor.isAffordable(node.gameState) && BoCommandQueenCreepTumor.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandQueenCreepTumor()); }
            for (int i = 0; i < node.gameState.nbHatcheries; i++)
            {
                if (node.isCommandRequired[(int)commandID.queenInject] && node.isCommandUnlocked[(int)commandID.queenInject] && BoCommandQueenInject.isAffordable(node.gameState) && BoCommandQueenInject.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandQueenInject(i)); }
            }

            //buildings - build everything at the main base
            for (int i = 0; i < 1 /*node.gameState.nbHatcheriesAtResources*/; i++)
            {
                if (node.isCommandRequired[(int)commandID.buildBanelingNest] && node.isCommandUnlocked[(int)commandID.buildBanelingNest] && BoCommandBuildBanelingNest.isAffordable(node.gameState) && BoCommandBuildBanelingNest.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildBanelingNest(i)); }
                if (node.isCommandRequired[(int)commandID.buildEvolutionChamber] && node.isCommandUnlocked[(int)commandID.buildEvolutionChamber] && BoCommandBuildEvolutionChamber.isAffordable(node.gameState) && BoCommandBuildEvolutionChamber.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildEvolutionChamber(i)); }
                if (node.isCommandRequired[(int)commandID.buildExtractor] && node.isCommandUnlocked[(int)commandID.buildExtractor] && BoCommandBuildExtractor.isAffordable(node.gameState) && BoCommandBuildExtractor.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildExtractor(i)); }
                if (node.isCommandRequired[(int)commandID.buildHatcheryAtResourcePatch] && node.isCommandUnlocked[(int)commandID.buildHatcheryAtResourcePatch] && BoCommandBuildHatcheryAtResourcePatch.isAffordable(node.gameState) && BoCommandBuildHatcheryAtResourcePatch.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildHatcheryAtResourcePatch(i)); }
                if (node.isCommandRequired[(int)commandID.buildHatcheryInBase] && node.isCommandUnlocked[(int)commandID.buildHatcheryInBase] && BoCommandBuildHatcheryInBase.isAffordable(node.gameState) && BoCommandBuildHatcheryInBase.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildHatcheryInBase(i)); }
                if (node.isCommandRequired[(int)commandID.buildHydraliskDen] && node.isCommandUnlocked[(int)commandID.buildHydraliskDen] && BoCommandBuildHydraliskDen.isAffordable(node.gameState) && BoCommandBuildHydraliskDen.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildHydraliskDen(i)); }
                if (node.isCommandRequired[(int)commandID.buildInfestationPit] && node.isCommandUnlocked[(int)commandID.buildInfestationPit] && BoCommandBuildInfestationPit.isAffordable(node.gameState) && BoCommandBuildInfestationPit.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildInfestationPit(i)); }
                if (node.isCommandRequired[(int)commandID.buildNydusNetwork] && node.isCommandUnlocked[(int)commandID.buildNydusNetwork] && BoCommandBuildNydusNetwork.isAffordable(node.gameState) && BoCommandBuildNydusNetwork.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildNydusNetwork(i)); }
                if (node.isCommandRequired[(int)commandID.buildRoachWarren] && node.isCommandUnlocked[(int)commandID.buildRoachWarren] && BoCommandBuildRoachWarren.isAffordable(node.gameState) && BoCommandBuildRoachWarren.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildRoachWarren(i)); }
                if (node.isCommandRequired[(int)commandID.buildSpawningPool] && node.isCommandUnlocked[(int)commandID.buildSpawningPool] && BoCommandBuildSpawningPool.isAffordable(node.gameState) && BoCommandBuildSpawningPool.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildSpawningPool(i)); }
                if (node.isCommandRequired[(int)commandID.buildSpineCrawler] && node.isCommandUnlocked[(int)commandID.buildSpineCrawler] && BoCommandBuildSpineCrawler.isAffordable(node.gameState) && BoCommandBuildSpineCrawler.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildSpineCrawler(i)); }
                if (node.isCommandRequired[(int)commandID.buildSpire] && node.isCommandUnlocked[(int)commandID.buildSpire] && BoCommandBuildSpire.isAffordable(node.gameState) && BoCommandBuildSpire.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildSpire(i)); }
                if (node.isCommandRequired[(int)commandID.buildSporeCrawler] && node.isCommandUnlocked[(int)commandID.buildSporeCrawler] && BoCommandBuildSporeCrawler.isAffordable(node.gameState) && BoCommandBuildSporeCrawler.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildSporeCrawler(i)); }
                if (node.isCommandRequired[(int)commandID.buildUltraliskCavern] && node.isCommandUnlocked[(int)commandID.buildUltraliskCavern] && BoCommandBuildUltraliskCavern.isAffordable(node.gameState) && BoCommandBuildUltraliskCavern.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildUltraliskCavern(i)); }
            }
            for (int i = 0; i < node.gameState.nbHatcheries; i++)
            {
                if (node.isCommandRequired[(int)commandID.buildHive] && node.isCommandUnlocked[(int)commandID.buildHive] && BoCommandBuildHive.isAffordable(node.gameState) && BoCommandBuildHive.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildHive(i)); }
                if (node.isCommandRequired[(int)commandID.buildLair] && node.isCommandUnlocked[(int)commandID.buildLair] && BoCommandBuildLair.isAffordable(node.gameState) && BoCommandBuildLair.isAvailable(node.gameState, i)) { possibleCommands.Add(new BoCommandBuildLair(i)); }
            }
            if (node.isCommandRequired[(int)commandID.buildGreaterSpire] && node.isCommandUnlocked[(int)commandID.buildGreaterSpire] && BoCommandBuildGreaterSpire.isAffordable(node.gameState) && BoCommandBuildGreaterSpire.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandBuildGreaterSpire()); }
            if (node.isCommandRequired[(int)commandID.buildNydusWorm] && node.isCommandUnlocked[(int)commandID.buildNydusWorm] && BoCommandBuildNydusWorm.isAffordable(node.gameState) && BoCommandBuildNydusWorm.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandBuildNydusWorm()); }

            //upgrades
            if (node.isCommandRequired[(int)commandID.upgradeAdrenalGlands] && node.isCommandUnlocked[(int)commandID.upgradeAdrenalGlands] && BoCommandUpgradeAdrenalGlands.isAffordable(node.gameState) && BoCommandUpgradeAdrenalGlands.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeAdrenalGlands()); }
            if (node.isCommandRequired[(int)commandID.upgradeBurrow] && node.isCommandUnlocked[(int)commandID.upgradeBurrow] && BoCommandUpgradeBurrow.isAffordable(node.gameState) && BoCommandUpgradeBurrow.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeBurrow()); }
            if (node.isCommandRequired[(int)commandID.upgradeCentrifugalHooks] && node.isCommandUnlocked[(int)commandID.upgradeCentrifugalHooks] && BoCommandUpgradeCentrifugalHooks.isAffordable(node.gameState) && BoCommandUpgradeCentrifugalHooks.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeCentrifugalHooks()); }
            if (node.isCommandRequired[(int)commandID.upgradeChitinousPlating] && node.isCommandUnlocked[(int)commandID.upgradeChitinousPlating] && BoCommandUpgradeChitinousPlating.isAffordable(node.gameState) && BoCommandUpgradeChitinousPlating.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeChitinousPlating()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerArmor1] && node.isCommandUnlocked[(int)commandID.upgradeFlyerArmor1] && BoCommandUpgradeFlyerArmor1.isAffordable(node.gameState) && BoCommandUpgradeFlyerArmor1.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerArmor1()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerArmor2] && node.isCommandUnlocked[(int)commandID.upgradeFlyerArmor2] && BoCommandUpgradeFlyerArmor2.isAffordable(node.gameState) && BoCommandUpgradeFlyerArmor2.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerArmor2()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerArmor3] && node.isCommandUnlocked[(int)commandID.upgradeFlyerArmor3] && BoCommandUpgradeFlyerArmor3.isAffordable(node.gameState) && BoCommandUpgradeFlyerArmor3.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerArmor3()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerAttacks1] && node.isCommandUnlocked[(int)commandID.upgradeFlyerAttacks1] && BoCommandUpgradeFlyerAttacks1.isAffordable(node.gameState) && BoCommandUpgradeFlyerAttacks1.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerAttacks1()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerAttacks2] && node.isCommandUnlocked[(int)commandID.upgradeFlyerAttacks2] && BoCommandUpgradeFlyerAttacks2.isAffordable(node.gameState) && BoCommandUpgradeFlyerAttacks2.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerAttacks2()); }
            if (node.isCommandRequired[(int)commandID.upgradeFlyerAttacks3] && node.isCommandUnlocked[(int)commandID.upgradeFlyerAttacks3] && BoCommandUpgradeFlyerAttacks3.isAffordable(node.gameState) && BoCommandUpgradeFlyerAttacks3.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeFlyerAttacks3()); }
            if (node.isCommandRequired[(int)commandID.upgradeGlialReconstitution] && node.isCommandUnlocked[(int)commandID.upgradeGlialReconstitution] && BoCommandUpgradeGlialReconstitution.isAffordable(node.gameState) && BoCommandUpgradeGlialReconstitution.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeGlialReconstitution()); }
            if (node.isCommandRequired[(int)commandID.upgradeGroovedSpines] && node.isCommandUnlocked[(int)commandID.upgradeGroovedSpines] && BoCommandUpgradeGroovedSpines.isAffordable(node.gameState) && BoCommandUpgradeGroovedSpines.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeGroovedSpines()); }
            if (node.isCommandRequired[(int)commandID.upgradeGroundArmor1] && node.isCommandUnlocked[(int)commandID.upgradeGroundArmor1] && BoCommandUpgradeGroundArmor1.isAffordable(node.gameState) && BoCommandUpgradeGroundArmor1.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeGroundArmor1()); }
            if (node.isCommandRequired[(int)commandID.upgradeGroundArmor2] && node.isCommandUnlocked[(int)commandID.upgradeGroundArmor2] && BoCommandUpgradeGroundArmor2.isAffordable(node.gameState) && BoCommandUpgradeGroundArmor2.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeGroundArmor2()); }
            if (node.isCommandRequired[(int)commandID.upgradeGroundArmor3] && node.isCommandUnlocked[(int)commandID.upgradeGroundArmor3] && BoCommandUpgradeGroundArmor3.isAffordable(node.gameState) && BoCommandUpgradeGroundArmor3.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeGroundArmor3()); }
            if (node.isCommandRequired[(int)commandID.upgradeMelee1] && node.isCommandUnlocked[(int)commandID.upgradeMelee1] && BoCommandUpgradeMelee1.isAffordable(node.gameState) && BoCommandUpgradeMelee1.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMelee1()); }
            if (node.isCommandRequired[(int)commandID.upgradeMelee2] && node.isCommandUnlocked[(int)commandID.upgradeMelee2] && BoCommandUpgradeMelee2.isAffordable(node.gameState) && BoCommandUpgradeMelee2.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMelee2()); }
            if (node.isCommandRequired[(int)commandID.upgradeMelee3] && node.isCommandUnlocked[(int)commandID.upgradeMelee3] && BoCommandUpgradeMelee3.isAffordable(node.gameState) && BoCommandUpgradeMelee3.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMelee3()); }
            if (node.isCommandRequired[(int)commandID.upgradeMetabolicBoost] && node.isCommandUnlocked[(int)commandID.upgradeMetabolicBoost] && BoCommandUpgradeMetabolicBoost.isAffordable(node.gameState) && BoCommandUpgradeMetabolicBoost.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMetabolicBoost()); }
            if (node.isCommandRequired[(int)commandID.upgradeMissile1] && node.isCommandUnlocked[(int)commandID.upgradeMissile1] && BoCommandUpgradeMissile1.isAffordable(node.gameState) && BoCommandUpgradeMissile1.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMissile1()); }
            if (node.isCommandRequired[(int)commandID.upgradeMissile2] && node.isCommandUnlocked[(int)commandID.upgradeMissile2] && BoCommandUpgradeMissile2.isAffordable(node.gameState) && BoCommandUpgradeMissile2.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMissile2()); }
            if (node.isCommandRequired[(int)commandID.upgradeMissile3] && node.isCommandUnlocked[(int)commandID.upgradeMissile3] && BoCommandUpgradeMissile3.isAffordable(node.gameState) && BoCommandUpgradeMissile3.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeMissile3()); }
            if (node.isCommandRequired[(int)commandID.upgradeNeuralParasite] && node.isCommandUnlocked[(int)commandID.upgradeNeuralParasite] && BoCommandUpgradeNeuralParasite.isAffordable(node.gameState) && BoCommandUpgradeNeuralParasite.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeNeuralParasite()); }
            if (node.isCommandRequired[(int)commandID.upgradePathogenGlands] && node.isCommandUnlocked[(int)commandID.upgradePathogenGlands] && BoCommandUpgradePathogenGlands.isAffordable(node.gameState) && BoCommandUpgradePathogenGlands.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradePathogenGlands()); }
            if (node.isCommandRequired[(int)commandID.upgradePneumatizedCarapace] && node.isCommandUnlocked[(int)commandID.upgradePneumatizedCarapace] && BoCommandUpgradePneumatizedCarapace.isAffordable(node.gameState) && BoCommandUpgradePneumatizedCarapace.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradePneumatizedCarapace()); }
            if (node.isCommandRequired[(int)commandID.upgradeTunnelingClaws] && node.isCommandUnlocked[(int)commandID.upgradeTunnelingClaws] && BoCommandUpgradeTunnelingClaws.isAffordable(node.gameState) && BoCommandUpgradeTunnelingClaws.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeTunnelingClaws()); }
            if (node.isCommandRequired[(int)commandID.upgradeVentralSacs] && node.isCommandUnlocked[(int)commandID.upgradeVentralSacs] && BoCommandUpgradeVentralSacs.isAffordable(node.gameState) && BoCommandUpgradeVentralSacs.isAvailable(node.gameState)) { possibleCommands.Add(new BoCommandUpgradeVentralSacs()); }

            //heuristics
            applyQueenHeuristic(possibleCommands);
            applyExtractorHeuristic(node, possibleCommands);
            applyCombatUnitsHeuristic(node, possibleCommands);

            return possibleCommands;
        }

        private void applyQueenHeuristic(List<BoCommand> possibleCommands)
        {
            //force queen commands to happen as soon as possible
            //check if queen commands are possible
            bool queenCommandsPossible = false;
            for (int i = 0; i < possibleCommands.Count; i++)
            {
                if (possibleCommands[i].getCommandID() == commandID.queenCreepTumor || possibleCommands[i].getCommandID() == commandID.queenInject)
                {
                    queenCommandsPossible = true;
                }
            }

            //remove everything that's not a queen command
            if (queenCommandsPossible)
            {
                //set null values
                for (int i = 0; i < possibleCommands.Count; i++)
                {
                    if (possibleCommands[i].getCommandID() != commandID.queenCreepTumor && possibleCommands[i].getCommandID() != commandID.queenInject)
                    {
                        possibleCommands[i] = null;
                    }
                }
                //remove null values
                for (int i = (possibleCommands.Count - 1); i >= 0; i--)
                {
                    if (possibleCommands[i] == null)
                    {
                        possibleCommands.RemoveAt(i);
                    }
                }
            }
        }
        private void applyExtractorHeuristic(BoDecisionNode node, List<BoCommand> possibleCommands)
        {
            //only build extractor after having built spawning pool
            int nbSpawningPools = 0;
            nbSpawningPools += node.gameState.nbBuildingsBuilt((int)Buildings.SpawningPool);
            nbSpawningPools += node.gameState.nbBuildingsBeingBuilt((int)Buildings.SpawningPool);

            if (nbSpawningPools == 0)
            {
                //set null values
                for (int i = 0; i < possibleCommands.Count; i++)
                {
                    if (possibleCommands[i].getCommandID() == commandID.buildExtractor)
                    {
                        possibleCommands[i] = null;
                    }
                }
                //remove null values
                for (int i = (possibleCommands.Count - 1); i >= 0; i--)
                {
                    if (possibleCommands[i] == null)
                    {
                        possibleCommands.RemoveAt(i);
                    }
                }
            }
        }
        private void applyCombatUnitsHeuristic(BoDecisionNode node, List<BoCommand> possibleCommands)
        {
            //build combat units in batches
            //check if more combat units (that cost larvae) are still required
            int nbCombatUnitsRequired = 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildCorruptor]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildHydralisk]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildInfestor]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildMutalisk]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildRoach]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildUltralisk]) ? 1 : 0;
            nbCombatUnitsRequired += (node.isCommandRequired[(int)commandID.buildZergling]) ? 1 : 0;

            //if we still need to build combat units that cost larvae
            if (nbCombatUnitsRequired > 0)
            {
                //check if we've started building combat units
                int nbCombatUnits = 0;
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Baneling);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Broodlord);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Corruptor);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Hydralisk);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Infestor);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Mutalisk);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Overseer);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Roach);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Ultralisk);
                nbCombatUnits += node.gameState.nbUnitsBuilt((int)Units.Zergling);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Baneling);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Broodlord);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Corruptor);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Hydralisk);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Infestor);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Mutalisk);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Overseer);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Roach);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Ultralisk);
                nbCombatUnits += node.gameState.nbUnitsBeingBuilt((int)Units.Zergling);

                //if we've started building combat units
                if (nbCombatUnits > 0)
                {
                    //remove all commands that don't build units that cost larvae
                    //set null values
                    for (int i = 0; i < possibleCommands.Count; i++)
                    {
                        bool isGood = false;
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildCorruptor);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildHydralisk);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildInfestor);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildMutalisk);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildRoach);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildUltralisk);
                        isGood = isGood || (possibleCommands[i].getCommandID() == commandID.buildZergling);

                        if (!isGood)
                        {
                            possibleCommands[i] = null;
                        }
                    }
                    //remove null values
                    for (int i = (possibleCommands.Count - 1); i >= 0; i--)
                    {
                        if (possibleCommands[i] == null)
                        {
                            possibleCommands.RemoveAt(i);
                        }
                    }
                }
            }
        }

        public BoGoal goal
        {
            get
            {
                return _goal;
            }
        }
        public bool isFastEnough(BoGameState gs)
        {
            if (_bestBuildOrder != null)
            {
                return (gs.nbTicksPassed <= _bestBuildOrder.nbTicksPassed);
            }
            else
            {
                return true;
            }
        }
        public void printBestBuildOrder()
        {
            if (_bestBuildOrder != null)
            {
                string output = string.Format("{0} * 50K games played \r\n\r\n{1}", _nbGamesPlayedPublished, _bestBuildOrder.toString());
                _GUI.Invoke(new updateOutputTextDelegate(_GUI.updateOutputText), new object[] { output });
            }
            else
            {
                string output = string.Format("{0} * 50K games played \r\n\r\nNo build orders found", _nbGamesPlayedPublished);
                _GUI.Invoke(new updateOutputTextDelegate(_GUI.updateOutputText), new object[] { output });
            }
        }

        public void updateStatistics()
        {
            _nbGamesPlayed++;
            if (_nbGamesPlayed > 50000)
            {
                _nbGamesPlayed = 0;
                _nbGamesPlayedPublished++;
                printBestBuildOrder();
            }
        }
        public void addBuildOrder(BoBuildOrder buildOrder)
        {
            if (_bestBuildOrder == null)
            {
                _bestBuildOrder = buildOrder;
            }
            else if (buildOrder.nbTicksPassed < _bestBuildOrder.nbTicksPassed)
            {
                _bestBuildOrder = buildOrder;
            }
            else if (buildOrder.nbTicksPassed == _bestBuildOrder.nbTicksPassed)
            {
                if ((buildOrder.mineralsMinedTotal + buildOrder.gasMinedTotal) > (_bestBuildOrder.mineralsMinedTotal + _bestBuildOrder.gasMinedTotal))
                {
                    _bestBuildOrder = buildOrder;
                }
                else if ((buildOrder.mineralsMinedTotal + buildOrder.gasMinedTotal) == (_bestBuildOrder.mineralsMinedTotal + _bestBuildOrder.gasMinedTotal))
                {
                    if (buildOrder.calcSimplicity() > _bestBuildOrder.calcSimplicity())
                    {
                        _bestBuildOrder = buildOrder;
                    }
                }
            }
        }
    }
}
